/******************************************************************************
 * Spine Runtimes License Agreement
 * Last updated January 1, 2020. Replaces all prior versions.
 *
 * Copyright (c) 2013-2020, Esoteric Software LLC
 *
 * Integration of the Spine Runtimes into software or otherwise creating
 * derivative works of the Spine Runtimes is permitted under the terms and
 * conditions of Section 2 of the Spine Editor License Agreement:
 * http://esotericsoftware.com/spine-editor-license
 *
 * Otherwise, it is permitted to integrate the Spine Runtimes into software
 * or otherwise create derivative works of the Spine Runtimes (collectively,
 * "Products"), provided that each user of the Products must obtain their own
 * Spine Editor license and redistribution of the Products in any form must
 * include this license and copyright notice.
 *
 * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
 * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/

// Contributed by: Mitch Thompson

using UnityEditor;
using UnityEngine;

namespace Spine.Unity.Editor {
    [CustomEditor(typeof(SkeletonMecanim))]
    [CanEditMultipleObjects]
    public class SkeletonMecanimInspector : SkeletonRendererInspector {
        public static bool mecanimSettingsFoldout;

        protected SerializedProperty autoReset;
        protected SerializedProperty useCustomMixMode;
        protected SerializedProperty layerMixModes;
        protected SerializedProperty layerBlendModes;

        protected override void OnEnable() {
            base.OnEnable();
            SerializedProperty mecanimTranslator = serializedObject.FindProperty("translator");
            autoReset = mecanimTranslator.FindPropertyRelative("autoReset");
            useCustomMixMode = mecanimTranslator.FindPropertyRelative("useCustomMixMode");
            layerMixModes = mecanimTranslator.FindPropertyRelative("layerMixModes");
            layerBlendModes = mecanimTranslator.FindPropertyRelative("layerBlendModes");
        }

        protected override void DrawInspectorGUI(bool multi) {
            AddRootMotionComponentIfEnabled();

            base.DrawInspectorGUI(multi);

            using (new SpineInspectorUtility.BoxScope()) {
                mecanimSettingsFoldout = EditorGUILayout.Foldout(mecanimSettingsFoldout, "Mecanim Translator");
                if (mecanimSettingsFoldout) {
                    EditorGUILayout.PropertyField(autoReset, new GUIContent("Auto Reset", "When set to true, the skeleton state is mixed out to setup-" + "pose when an animation finishes, according to the " + "animation's keyed items."));

                    EditorGUILayout.PropertyField(useCustomMixMode, new GUIContent("Custom MixMode", "When disabled, the recommended MixMode is used according to the layer blend mode. Enable to specify a custom MixMode for each Mecanim layer."));

                    if (useCustomMixMode.hasMultipleDifferentValues || useCustomMixMode.boolValue == true) {
                        DrawLayerSettings();
                        EditorGUILayout.Space();
                    }
                }
            }
        }

        protected void AddRootMotionComponentIfEnabled() {
            foreach (var t in targets) {
                var component = t as Component;
                var animator = component.GetComponent<Animator>();
                if (animator != null && animator.applyRootMotion) {
                    if (component.GetComponent<SkeletonMecanimRootMotion>() == null) {
                        component.gameObject.AddComponent<SkeletonMecanimRootMotion>();
                    }
                }
            }
        }

        protected void DrawLayerSettings() {
            string[] layerNames = GetLayerNames();
            float widthLayerColumn = 140;
            float widthMixColumn = 84;

            using (new GUILayout.HorizontalScope()) {
                var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
                rect.width = widthLayerColumn;
                EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mecanim Layer"), EditorStyles.boldLabel);

                var savedIndent = EditorGUI.indentLevel;
                EditorGUI.indentLevel = 0;

                rect.position += new Vector2(rect.width, 0);
                rect.width = widthMixColumn;
                EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mix Mode"), EditorStyles.boldLabel);

                EditorGUI.indentLevel = savedIndent;
            }

            using (new SpineInspectorUtility.IndentScope()) {
                int layerCount = layerMixModes.arraySize;
                for (int i = 0; i < layerCount; ++i) {
                    using (new GUILayout.HorizontalScope()) {
                        string layerName = i < layerNames.Length ? layerNames[i] : ("Layer " + i);

                        var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
                        rect.width = widthLayerColumn;
                        EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent(layerName));

                        var savedIndent = EditorGUI.indentLevel;
                        EditorGUI.indentLevel = 0;

                        var mixMode = layerMixModes.GetArrayElementAtIndex(i);
                        rect.position += new Vector2(rect.width, 0);
                        rect.width = widthMixColumn;
                        EditorGUI.PropertyField(rect, mixMode, GUIContent.none);

                        EditorGUI.indentLevel = savedIndent;
                    }
                }
            }
        }

        protected string[] GetLayerNames() {
            int maxLayerCount = 0;
            int maxIndex = 0;
            for (int i = 0; i < targets.Length; ++i) {
                var skeletonMecanim = ((SkeletonMecanim)targets[i]);
                int count = skeletonMecanim.Translator.MecanimLayerCount;
                if (count > maxLayerCount) {
                    maxLayerCount = count;
                    maxIndex = i;
                }
            }
            if (maxLayerCount == 0)
                return new string[0];
            var skeletonMecanimMaxLayers = ((SkeletonMecanim)targets[maxIndex]);
            return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
        }
    }
}
